﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace MonoStrategy
{
    public abstract class WorkerBuilding : BaseBuilding, ISuspendableBuilding
    {
        internal WorkerBuilding(BuildingManager inParent, BuildingConfig inConfig, Point inPosition) : base(inParent, inConfig, inPosition)
        {
            if (!SpawnPoint.HasValue)
                throw new ArgumentException("Buildable \"" + inConfig.Name + "\" derives from \"WorkerBuilding\" but has no spawn point!");
        }
    }

    public abstract class BaseBuilding : PositionTracker
    {
        protected static readonly CrossRandom m_Random = new CrossRandom(0);
        internal LinkedListNode<BaseBuilding> Node { get; set; }
        internal List<GenericResourceStack> Providers { get; private set; }
        public IEnumerable<GenericResourceStack> ProvidersReadonly { get { return Providers.AsReadOnly(); } }
        internal List<ResourceStackConfig> ProviderConfigs { get; private set; }
        internal List<GenericResourceStack> Queries { get; private set; }
        public IEnumerable<GenericResourceStack> QueriesReadonly { get { return Queries.AsReadOnly(); } }
        internal List<ResourceStackConfig> QueriesConfig { get; private set; }
        internal long ProductionStart = Int64.MinValue / 2;
        private readonly UniqueIDObject m_Unique;
        internal long UniqueID { get { return m_Unique.UniqueID; } }
        internal int[] QueriesPreload { get; private set; }
        internal long ProductionTime { get; private set; }
        public Boolean IsSuspended { get; internal set; }
        public Point WorkingArea { get; internal set; }
        internal bool UseQualityIndex = false;

        public Boolean IsReady { get; private set; }
        internal BuildingManager Parent { get; private set; }
        public BuildingConfig Config { get; private set; }
        internal CyclePoint? SpawnPoint { get; private set; }

        public Point Center
        {
            get
            {
                return new Point(
                    Position.XGrid + Config.GridWidth / 2,
                    Position.YGrid + Config.GridHeight / 2);
            }
        }

        public event Procedure<BaseBuilding> OnStateChanged;

        internal virtual bool Update()
        {
            return !IsSuspended;
        }

        internal void MarkAsReady()
        {
            if (IsReady)
                return;

            IsReady = true;

            RaiseStateChanged();
        }

        protected void RaiseStateChanged()
        {
            if (OnStateChanged != null)
                OnStateChanged(this);
        }

        internal void RaisePriority()
        {
            foreach (var query in Queries)
            {
                if (query == null)
                    continue;

                Parent.ResMgr.RaisePriority(query);
            }
        }

        internal void LowerPriority()
        {
            foreach (var query in Queries)
            {
                if (query == null)
                    continue;

                Parent.ResMgr.LowerPriority(query);
            }
        }

        internal BaseBuilding(BuildingManager inParent, BuildingConfig inConfig, Point inPosition)
        {
            m_Unique = new UniqueIDObject(this);
            Parent = inParent;
            Config = inConfig;
            Providers = new List<GenericResourceStack>();
            Queries = new List<GenericResourceStack>();
            Position = CyclePoint.FromGrid(inPosition);
            ProviderConfigs = new List<ResourceStackConfig>();
            QueriesConfig = new List<ResourceStackConfig>();
            ProductionTime = inConfig.ProductionTimeMillis;

            foreach (var stackCfg in Config.ResourceStacks)
            {
                if (stackCfg.Type != Resource.Max)
                    continue;

                CyclePoint stackPos = CyclePoint.FromGrid(
                             stackCfg.Position.X + Position.X,
                             stackCfg.Position.Y + Position.Y);

                if (!SpawnPoint.HasValue)
                    SpawnPoint = stackPos;
            }
        }

        internal bool HasFreeProvider()
        {
            return (Providers.Any(e => { if (e == null) return true; else return e.Available < e.MaxCount; }));
        }

        internal virtual void CreateResourceStacks()
        {
            // create resource stacks for building
            var tmpQueries = new List<KeyValuePair<ResourceStackConfig, GenericResourceStack>>();

            foreach (var stackCfg in Config.ResourceStacks)
            {
                GenericResourceStack stack = null;
                CyclePoint stackPos = CyclePoint.FromGrid(
                             stackCfg.Position.X + Position.X,
                             stackCfg.Position.Y + Position.Y);

                if (stackCfg.Type != Resource.Max)
                {
                    stack = Parent.ResMgr.AddResourceStack(
                            this,
                            stackPos,
                            stackCfg.Direction,
                            stackCfg.Type,
                            stackCfg.MaxStackSize);

                    if (stackCfg.Direction == ResStackType.Provider)
                    {
                        Providers.Add(stack);
                        ProviderConfigs.Add(stackCfg);
                    }
                }
                
                if (stackCfg.Direction == ResStackType.Query)
                {
                    tmpQueries.Add(new KeyValuePair<ResourceStackConfig, GenericResourceStack>(stackCfg, stack));
                }
            }

            tmpQueries = tmpQueries.OrderBy(e => e.Key.QualityIndex).ToList();
            QueriesConfig = tmpQueries.Select(e => e.Key).ToList();
            Queries = tmpQueries.Select(e => e.Value).ToList();
            UseQualityIndex = QueriesConfig.Any(e => e.QualityIndex > 0);

            QueriesPreload = new int[Queries.Count];
        }
    }
}
